package cn.gotom.svg;


class TextScanner
{
	protected String input;
	protected int position = 0;

	public TextScanner(String input)
	{
		this.input = input.trim();
	}

	/**
	 * Returns true if we have reached the end of the input.
	 */
	public boolean empty()
	{
		return (position == input.length());
	}

	protected boolean isWhitespace(int c)
	{
		return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
	}

	public void skipWhitespace()
	{
		while (position < input.length())
		{
			if (!isWhitespace(input.charAt(position)))
				break;
			position++;
		}
	}

	protected boolean isEOL(int c)
	{
		return (c == '\n' || c == '\r');
	}

	// Skip the sequence: <space>*(<comma><space>)?
	// Returns true if we found a comma in there.
	public boolean skipCommaWhitespace()
	{
		skipWhitespace();
		if (position == input.length())
			return false;
		if (!(input.charAt(position) == ','))
			return false;
		position++;
		skipWhitespace();
		return true;
	}

	public Float nextFloat()
	{
		int floatEnd = scanForFloat();
		if (floatEnd == position)
			return null;
		Float result = Float.parseFloat(input.substring(position, floatEnd));
		position = floatEnd;
		return result;
	}

	/*
	 * Scans for a comma-whitespace sequence with a float following it. If found, the float is returned. Otherwise null is returned and the scan position left as it was.
	 */
	public Float possibleNextFloat()
	{
		int start = position;
		skipCommaWhitespace();
		Float result = nextFloat();
		if (result != null)
			return result;
		position = start;
		return null;
	}

	/*
	 * Scans for comma-whitespace sequence with a float following it. But only if the provided 'lastFloat' (representing the last coord scanned was non-null (ie parsed correctly).
	 */
	public Float checkedNextFloat(Object lastRead)
	{
		if (lastRead == null)
		{
			return null;
		}
		skipCommaWhitespace();
		return nextFloat();
	}

	public Integer nextInteger()
	{
		int intEnd = scanForInteger();
		// System.out.println("nextFloat: "+position+" "+floatEnd);
		if (intEnd == position)
			return null;
		Integer result = Integer.parseInt(input.substring(position, intEnd));
		position = intEnd;
		return result;
	}

	public Integer nextChar()
	{
		if (position == input.length())
			return null;
		return Integer.valueOf(input.charAt(position++));
	}

	/*
	 * Scan for a 'flag'. A flag is a '0' or '1' digit character.
	 */
	public Boolean nextFlag()
	{
		if (position == input.length())
			return null;
		char ch = input.charAt(position);
		if (ch == '0' || ch == '1')
		{
			position++;
			return Boolean.valueOf(ch == '1');
		}
		return null;
	}

	/*
	 * Like checkedNextFloat, but reads a flag (see path definition parser)
	 */
	public Boolean checkedNextFlag(Object lastRead)
	{
		if (lastRead == null)
		{
			return null;
		}
		skipCommaWhitespace();
		return nextFlag();
	}

	public boolean consume(char ch)
	{
		boolean found = (position < input.length() && input.charAt(position) == ch);
		if (found)
			position++;
		return found;
	}

	public boolean consume(String str)
	{
		int len = str.length();
		boolean found = (position <= (input.length() - len) && input.substring(position, position + len).equals(str));
		if (found)
			position += len;
		return found;
	}

	protected int advanceChar()
	{
		if (position == input.length())
			return -1;
		position++;
		if (position < input.length())
			return input.charAt(position);
		else
			return -1;
	}

	/*
	 * Scans the input starting immediately at 'position' for the next token. A token is a sequence of characters terminating at a whitespace character. Note that this routine only checks for whitespace characters. Use nextToken(char) if token might end with another character.
	 */
	public String nextToken()
	{
		return nextToken(' ');
	}

	/*
	 * Scans the input starting immediately at 'position' for the next token. A token is a sequence of characters terminating at either a whitespace character or the supplied terminating character.
	 */
	public String nextToken(char terminator)
	{
		if (empty())
			return null;

		int ch = input.charAt(position);
		if (isWhitespace(ch) || ch == terminator)
			return null;

		int start = position;
		ch = advanceChar();
		while (ch != -1 && ch != terminator && !isWhitespace(ch))
		{
			ch = advanceChar();
		}
		return input.substring(start, position);
	}

	/*
	 * Scans the input starting immediately at 'position' for the a sequence of letter characters terminated by an open bracket. The function name is returned.
	 */
	public String nextFunction()
	{
		if (empty())
			return null;
		int start = position;

		int ch = input.charAt(position);
		while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
			ch = advanceChar();
		int end = position;
		while (isWhitespace(ch))
			ch = advanceChar();
		if (ch == '(')
		{
			position++;
			return input.substring(start, end);
		}
		position = start;
		return null;
	}

	/*
	 * Scans the input starting immediately at 'position' for a floating point number. If one is found, the end position of the float will be returned. If the returned value is the same as 'position' then no float was found.
	 */
	private int scanForFloat()
	{
		if (empty())
			return position;
		int lastValidPos = position;
		int start = position;

		int ch = input.charAt(position);
		// Check whole part of mantissa
		if (ch == '-' || ch == '+')
			ch = advanceChar();
		if (Character.isDigit(ch))
		{
			lastValidPos = position + 1;
			ch = advanceChar();
			while (Character.isDigit(ch))
			{
				lastValidPos = position + 1;
				ch = advanceChar();
			}
		}
		// Fraction or exponent starts here
		if (ch == '.')
		{
			lastValidPos = position + 1;
			ch = advanceChar();
			while (Character.isDigit(ch))
			{
				lastValidPos = position + 1;
				ch = advanceChar();
			}
		}
		// Exponent
		if (ch == 'e' || ch == 'E')
		{
			ch = advanceChar();
			if (ch == '-' || ch == '+')
				ch = advanceChar();
			if (Character.isDigit(ch))
			{
				lastValidPos = position + 1;
				ch = advanceChar();
				while (Character.isDigit(ch))
				{
					lastValidPos = position + 1;
					ch = advanceChar();
				}
			}
		}

		position = start;
		return lastValidPos;
	}

	/*
	 * Scans the input starting immediately at 'position' for an integer number. If one is found, the end position of the float will be returned. If the returned value is the same as 'position' then no number was found.
	 */
	private int scanForInteger()
	{
		if (empty())
			return position;
		int lastValidPos = position;
		int start = position;

		int ch = input.charAt(position);
		// Check whole part of mantissa
		if (ch == '-' || ch == '+')
			ch = advanceChar();
		if (Character.isDigit(ch))
		{
			lastValidPos = position + 1;
			ch = advanceChar();
			while (Character.isDigit(ch))
			{
				lastValidPos = position + 1;
				ch = advanceChar();
			}
		}

		position = start;
		return lastValidPos;
	}

	/*
	 * Get the next few chars. Mainly used for error messages.
	 */
	public String ahead()
	{
		int start = position;
		while (!empty() && !isWhitespace(input.charAt(position)))
			position++;
		String str = input.substring(start, position);
		position = start;
		return str;
	}

	/*
	 * Check whether the next character is a letter.
	 */
	public boolean hasLetter()
	{
		if (position == input.length())
			return false;
		char ch = input.charAt(position);
		return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));
	}

	/*
	 * Extract a quoted string from the input.
	 */
	public String nextQuotedString()
	{
		if (empty())
			return null;
		int start = position;
		int ch = input.charAt(position);
		int endQuote = ch;
		if (ch != '\'' && ch != '"')
			return null;
		ch = advanceChar();
		while (ch != -1 && ch != endQuote)
			ch = advanceChar();
		if (ch == -1)
		{
			position = start;
			return null;
		}
		position++;
		return input.substring(start + 1, position - 1);
	}

	/*
	 * Return the remaining input as a string.
	 */
	public String restOfText()
	{
		if (empty())
			return null;

		int start = position;
		position = input.length();
		return input.substring(start);
	}

}
